# coding: utf-8
import numpy as np
from random import shuffle

from TSP import TSP


class EdgeSet:
    def __init__(self, n):
        self.n = n  # the number of cities
        self.link = np.zeros(shape=(self.n + 1, 2), dtype=int)  # self.link[i][] the two adjacent cities of city i
        self.value = None  # the road length of TSP

    def __eq__(self, other):
        if self.n != other.n or abs(self.value - other.value) > 1e-4:
            return False
        for i in range(1, self.n + 1):
            if not ((self.link[0] == other.link[0] and self.link[1] == other.link[1])
                    or (self.link[0] == other.link[1] and self.link[1] == other.link[0])):
                return False
        return True

    def rand_init(self):
        road = np.random.permutation(np.arange(1, self.n + 1))
        for i in range(1, len(road) - 1):
            self.link[road[i]][0] = road[i - 1]
            self.link[road[i]][1] = road[i + 1]
        self.link[road[0]][0], self.link[road[0]][1] = road[-1], road[1]
        self.link[road[-1]][0], self.link[road[-1]][1] = road[-2], road[0]

    def _2_opt(self, Distance):
        pass

    def evaluate(self, Distance):
        self.value = sum(Distance[i][link[0]] + Distance[i][link[1]] for i, link in enumerate(self.link)) / 2


class EAXOptimizer:
    def __init__(self, data):
        self.V = data.cities  # city list
        self.n = len(self.V)  # the number of cities
        self.Distance = data.distance_matrix  # distance matrix
        self.NP = 100  # number of population
        self.NS = 30  # number of offspring
        self.Population = None

    def optimize(self, iteration=1000):
        self.Population = [EdgeSet(n=self.n) for _ in range(self.NP)]
        for individual in self.Population:
            individual.rand_init()
            individual.evaluate(self.Distance)
        for it in range(1, iteration + 1):
            shuffle(self.Population)
            for i in range(0, self.NP - 1):
                pA, pB = self.Population[i], self.Population[i + 1]
                offspring = self.cross(pA, pB)
                self.Population[i] = self.select(candidates=offspring + [pA])

    def cross(self, pA, pB):
        return list()

    def select(self, candidates):
        return 1


tsp_data = TSP(path=f'../../data/TSPLIB/att48.tsp.txt')

E1 = EdgeSet(n=tsp_data.n)
E1.rand_init()
E1.evaluate(Distance=tsp_data.distance_matrix)
